home *** CD-ROM | disk | FTP | other *** search
/ Aminet 5 / Aminet 5 - March 1995.iso / Aminet / mus / edit / AlgoRhythms.lha / AlgoRhythms / Source / record.c < prev    next >
C/C++ Source or Header  |  1994-11-25  |  22KB  |  770 lines

  1. /* Record.c
  2.     Copyright (c) 1990,1991,1992,1993 by Thomas E. Janzen
  3.     All Rights Reserved
  4.  
  5.     THIS SOFTWARE IS FURNISHED FREE OF CHARGE FOR STUDY AND USE AND MAY
  6.     BE COPIED ONLY FOR PERSONAL USE OR COMPLETELY AS OFFERED WITH NO
  7.     CHANGES FOR FREE DISTRIBUTION.  NO TITLE TO AND OWNERSHIP OF THE
  8.     SOFTWARE IS HEREBY TRANSFERRED.  THOMAS E. JANZEN ASSUMES NO 
  9.     RESPONSIBILITY FOR THE USE OR RELIABILITY OF THIS SOFTWARE.
  10.  
  11.     Thomas E. Janzen
  12.     208A Olde Derby Road
  13.     Norwood, MA  02062-1761
  14.     (617)769-7733
  15.  
  16. **  FACILITY:
  17. **
  18. **    AlgoRhythms music improviser on Commodore (TM) Amiga (TM)
  19. **    compiled with SAS/C Amiga Compiler 6.50 
  20. **
  21. **  ABSTRACT:
  22. **
  23. **       Record.c contains functions for MIDI event recording and saving
  24. **    
  25. **  AUTHORS: Thomas E. Janzen
  26. **
  27. **  CREATION DATE:    9-DEC-1991
  28. **
  29. **  MODIFICATION HISTORY:
  30. **    DATE    NAME    DESCRIPTION
  31. **  4 Jan 92 TEJ  last changes for 2.0
  32. */
  33.  
  34. #include <stdlib.h>
  35. #include <stdio.h>
  36. #include <intuition/intuition.h>
  37. #include <proto/timer.h>
  38. #include <ctype.h>
  39. #include <string.h>
  40. #include "AlgoRhythms.h"
  41. #include "Menus.h"
  42. #include "record.h"
  43.  
  44. #define NOTES_PER_BLOCK 1024
  45. #define MULTITRAK       1
  46. #define MIDIHDRLEN      14
  47. #define MIDITRKHDRLEN   8
  48. #define TICKS_PER_QUARTER 240
  49. #define NOTEON          0X90
  50. #define META_EVENT      0XFF
  51. #define TEXT_EVENT      0X01
  52. #define TRACK_NAME      0X03
  53. #define EOT_EVENT       0X2F
  54. #define TEMPO_EVENT     0X51
  55. #define TIME_SIG_EVENT  0X58
  56. #define PACK_BYTES(A, B, C, D)  (D | (C << 8) | (B << 16) | (A << 24))
  57.  
  58. struct Note_Record_Struct
  59. {
  60.     struct timeval  nr_r_event_time;
  61.     unsigned char   nr_uc_channel,
  62.                     nr_uc_pitch,
  63.                     nr_uc_velocity;
  64. };
  65. typedef struct Note_Record_Struct NOTE_RECORD_TYPE;
  66.  
  67. typedef struct Page_List_Struct PAGE_LIST_TYPE;
  68. struct Page_List_Struct
  69. {
  70.     PAGE_LIST_TYPE   *pl_r_succ,
  71.                      *pl_r_pred;
  72.     NOTE_RECORD_TYPE *pl_r_page;
  73. };
  74.  
  75. typedef struct midi_note_struct MIDI_NOTE_TYPE;
  76. struct midi_note_struct
  77. {
  78.     unsigned char  mn_uc_sts_chanl,
  79.                    mn_uc_note_num,
  80.                    nm_velocity;
  81. };
  82.  
  83. typedef struct MIDI_Header MIDI_HEADER_TYPE;
  84. struct MIDI_Header
  85. {
  86.     unsigned int   chunk_type,
  87.                    chunk_len;
  88.     unsigned short int  format,
  89.                         num_tracks,
  90.                         division; /* of time, usu. 24 MIDI ticks */
  91. };
  92.  
  93. typedef struct MIDI_Track MIDI_TRACK_TYPE;
  94. struct MIDI_Track
  95. {
  96.     unsigned int chunk_type,
  97.                  chunk_len;
  98. };
  99.  
  100. static unsigned char *midi_score = NULL;
  101. static unsigned int total_notes_qty = 0;
  102.  
  103. static PAGE_LIST_TYPE page_list_head = {NULL, NULL, NULL};
  104. static PAGE_LIST_TYPE *current_list_ptr = NULL;
  105. unsigned int recording = FALSE;
  106. static int page_note_counter = 0;
  107.  
  108. static void add_page(void);
  109.  
  110. static int variable_len_values(int number, unsigned char *array);
  111. static void insert_meta_event(unsigned int *midi_index,
  112.                               unsigned char *midi_score,
  113.                               const unsigned char event_type,
  114.                               const unsigned int event_len);
  115. static void store_a_note(  unsigned int *midi_index,
  116.                             unsigned char *midi_score,
  117.                             unsigned char current_channel,
  118.                             unsigned char current_dynamic,
  119.                             unsigned char pitch,
  120.                             unsigned char *running_sts);
  121.  
  122. unsigned int record_init(void)
  123. /*
  124. ** FUNCTIONAL DESCRIPTION:
  125. **  Sets up the recording data structures
  126. **
  127. ** RETURN VALUE:
  128. **      description: 
  129. **        data_type: int
  130. **
  131. ** DESIGN:
  132. **
  133. **  ROUTINE
  134. **  : IF page_list_head.pl_r_page == NULL
  135. **  : : page_list_head.pl_r_page = malloc()
  136. **  : : IF malloc failed
  137. **  : : : return FALSE
  138. **  : : ENDIF
  139. **  : : initialize list head pred and succ to NULL
  140. **  : : current_list_ptr = &page_list_head
  141. **  : : total_notes_qty = 0
  142. **  : ENDIF
  143. **  : return TRUE
  144. **  ENDROUTINE
  145. **
  146. */
  147. {
  148.     if (NULL == page_list_head.pl_r_page)
  149.     {
  150.         page_list_head.pl_r_page 
  151.             = malloc(sizeof(NOTE_RECORD_TYPE) * NOTES_PER_BLOCK);
  152.         if (NULL == page_list_head.pl_r_page)
  153.         {
  154.             return FALSE;   /* recording won't turn on if malloc failed */
  155.         }
  156.         page_list_head.pl_r_succ = NULL;
  157.         page_list_head.pl_r_pred = NULL;
  158.         current_list_ptr = &page_list_head;
  159.         total_notes_qty = 0;
  160.         page_note_counter = 0;
  161.     }
  162.     return TRUE;
  163. }
  164.  
  165. int record_note_event(NOTE_EVENT_TYPE *note_event)
  166. /*
  167. ** FUNCTIONAL DESCRIPTION:
  168. **  Stores a play note event in memory for later writing to a MIDI file
  169. ** RETURN VALUE:
  170. **      description: TRUE if successful
  171. **        data_type: int
  172. **
  173. ** ARGUMENTS:
  174. **
  175. **  note_event-
  176. **         description: a single voice
  177. **           data_type: pointer to NOTE_EVENT_TYPE
  178. **              access: read/write only
  179. **
  180. ** DESIGN:
  181. **
  182. **  ROUTINE record_note_event
  183. **  : IF current_list_ptr->pl_r_page == NULL
  184. **  : : current_list_ptr->pl_r_page = malloc()
  185. **  : : IF malloc failed
  186. **  : : : turn off recording
  187. **  : : : clear_record()
  188. **  : : : return FALSE
  189. **  : : ENDIF
  190. **  : ENDIF
  191. **  : IF this block of notes is full
  192. **  : : add_page()
  193. **  : : clear note_event_index
  194. **  : ENDIF
  195. **  : copy pitch, channel and dynamic from note_event to note_record
  196. **  : IF note is playing
  197. **  : : copy note_event start_time to note_record event_time
  198. **  : ELSE the note is being turned off
  199. **  : : copy note_event stop_time to note_record event_time
  200. **  : ENDIF
  201. **  : append note_record into the data array pointed to by linked list
  202. **  : increment note_event_index
  203. **  : increment notes_qty
  204. **  : return TRUE
  205. **  ENDROUTINE
  206. **
  207. */
  208. {
  209.     auto NOTE_RECORD_TYPE note_record;
  210.  
  211.     if (NULL == current_list_ptr->pl_r_page)
  212.     {
  213.         current_list_ptr->pl_r_page 
  214.             = malloc(sizeof(NOTE_RECORD_TYPE) * NOTES_PER_BLOCK);
  215.         if (NULL == current_list_ptr->pl_r_page)
  216.         {
  217.             recording = FALSE;
  218.             clear_record();
  219.             /* post an error message */
  220.             return FALSE;
  221.         }
  222.     }   
  223.     if (page_note_counter >= NOTES_PER_BLOCK)
  224.     {
  225.         add_page();
  226.     }
  227.     note_record.nr_uc_pitch    = note_event->nv_i_cur_pitch;
  228.     note_record.nr_uc_channel  = note_event->nv_i_channel;
  229.     note_record.nr_uc_velocity = note_event->nv_i_dynamic;
  230.     if (note_event->nv_i_playing) /* it's a note on */
  231.     {
  232.         note_record.nr_r_event_time = note_event->nv_r_start_time;
  233.     }
  234.     else
  235.     {
  236.         note_record.nr_r_event_time = note_event->nv_r_stop_time;
  237.     }
  238.     memcpy(&(current_list_ptr->pl_r_page[page_note_counter]),
  239.                 ¬e_record, sizeof(NOTE_RECORD_TYPE));
  240.     page_note_counter++;
  241.     total_notes_qty++;
  242.     return TRUE;
  243. }
  244.  
  245. static void add_page(void)
  246. /*
  247. ** FUNCTIONAL DESCRIPTION:
  248. **  appends another page for MIDI data to linked list
  249. **
  250. ** DESIGN:
  251. **
  252. **  ROUTINE add_page
  253. **  : current_list_ptr->pl_r_succ = malloc()
  254. **  : IF malloc failed
  255. **  : : stop recording
  256. **  : : clear_record()
  257. **  : : return
  258. **  : ENDIF
  259. **  : current_list_ptr->pl_r_succ->pl_r_pred = current_list_ptr
  260. **  : current_list_ptr = current_list_ptr->pl_r_succ
  261. **  : current_list_ptr->pl_r_page = malloc(page)
  262. **  : IF malloc failed
  263. **  : : stop recording
  264. **  : : clear_record()
  265. **  : : return
  266. **  : ENDIF
  267. **  : current_list_ptr->pl_r_succ = NULL
  268. **  : return
  269. **  ENDROUTINE
  270. **
  271. */
  272. {
  273.     current_list_ptr->pl_r_succ = malloc(sizeof(PAGE_LIST_TYPE));
  274.     if (NULL == current_list_ptr->pl_r_succ)
  275.     {
  276.         recording = FALSE;
  277.         clear_record();
  278.         return;
  279.     }
  280.     current_list_ptr->pl_r_succ->pl_r_pred = current_list_ptr;
  281.     current_list_ptr = current_list_ptr->pl_r_succ;
  282.     current_list_ptr->pl_r_page 
  283.         = malloc(sizeof(NOTE_RECORD_TYPE) * NOTES_PER_BLOCK);
  284.     if (NULL == current_list_ptr->pl_r_page)
  285.     {
  286.         recording = FALSE;
  287.         clear_record();
  288.         /* post an error message */
  289.         return;
  290.     }
  291.     current_list_ptr->pl_r_succ = NULL;
  292.     page_note_counter = 0;
  293.     return;
  294. }
  295.  
  296. void erase_recording(void)
  297. /*
  298. ** FUNCTIONAL DESCRIPTION:
  299. **  Dissassembles the linked list of recorded MIDI data.
  300. **
  301. ** DESIGN:
  302. **
  303. **  ROUTINE erase_recording
  304. **  : tmp_entry = &page_list_head
  305. **  : WHILE not at end of page list
  306. **  : : tmp_entry = tmp_entry->pl_r_succ
  307. **  : ENDWHILE
  308. **  : WHILE not at beginning of list
  309. **  : : free page of this entry
  310. **  : : step back one list entry
  311. **  : : free successor of this list entry
  312. **  : ENDWHILE
  313. **  : zero out total_notes_qty
  314. **  ENDROUTINE
  315. **
  316. */
  317. {
  318.     auto PAGE_LIST_TYPE *tmp_entry;
  319.  
  320.     tmp_entry = &page_list_head;
  321.     
  322.     while (tmp_entry->pl_r_succ != NULL)
  323.     {
  324.         tmp_entry = tmp_entry->pl_r_succ;
  325.     }
  326.     /* 
  327.     ** we are now pointing at the last list entry
  328.     ** so unwind the data 
  329.     */
  330.     while (tmp_entry->pl_r_pred != NULL)
  331.     {
  332.         free(tmp_entry->pl_r_page);
  333.         tmp_entry->pl_r_page = NULL;
  334.         tmp_entry = tmp_entry->pl_r_pred;
  335.         free(tmp_entry->pl_r_succ);
  336.         tmp_entry->pl_r_succ = NULL;
  337.     }
  338.     total_notes_qty = 0;
  339.     /*
  340.     ** We should now point to the head of the list 
  341.     */
  342.     return;
  343. }
  344.  
  345. int write_midi(char *midi_file_nam)
  346. /*
  347. ** FUNCTIONAL DESCRIPTION:
  348. **  Write the recording buffer to a MIDI file
  349. **
  350. ** RETURN VALUE:
  351. **      description: TRUE if successful
  352. **        data_type: int
  353. **
  354. ** ARGUMENTS:
  355. **
  356. **  midi_file_nam-
  357. **         description: name of MIDI file to which to write the record buff
  358. **           data_type: pointer to char
  359. **              access: read/write only
  360. **
  361. ** DESIGN:
  362. **
  363. **  ROUTINE write_midi
  364. **  : local_list_ptr = &page_list_head
  365. **  : midi_index = 0
  366. **  : midi_score = malloc(room for all of the notes and then some)
  367. **  : IF malloc failed
  368. **  : : return FALSE
  369. **  : ENDIF
  370. **  : midi_score[midi_index] = 0
  371. **  : midi_index++
  372. **  : insert track name meta event into midi_score
  373. **  : append 0 delay byte to midi_score
  374. **  : append tempo meta event to midi_score
  375. **  : append 0 delay byte to midi_score
  376. **  : append 4/4 meter meta event to midi_score
  377. **  : FOR all the recorded notes
  378. **  : : IF ran out of notes in this block
  379. **  : : : go to next block
  380. **  : : : IF this list member is NULL OR its page pointer is NULL
  381. **  : : : : free midi_score
  382. **  : : : : return FALSE
  383. **  : : : ENDIF
  384. **  : : : intra_block_index = 0
  385. **  : : ENDIF
  386. **  : : copy note_record from the linked list page
  387. **  : : increment intra_block_index
  388. **  : : copy pitch, channel and velocity from note_record to note_event
  389. **  : : tmp_time = note_record event_time
  390. **  : : IF local_total_notes_qty == 0
  391. **  : : : last_time = tmp_time
  392. **  : : ENDIF
  393. **  : : IF tmp_time > last_time
  394. **  : : : fix last_time and time_int
  395. **  : : ELSE
  396. **  : : : tmp_time = tmp_time - last_time
  397. **  : : : last_time = note_record event_time
  398. **  : : : convert tmp_time into MIDI delay
  399. **  : : ENDIF
  400. **  : : store the note in midi_score
  401. **  : ENDFOR
  402. **  : add a trailing delay
  403. **  : increment midi_index
  404. **  : add end of track MIDI meta event in midi_score
  405. **  : open midi file
  406. **  : IF file open failed
  407. **  : : free midi_score
  408. **  : : return FALSE
  409. **  : ENDIF
  410. **  : write MIDI header to the midi file
  411. **  : write track type chunk to file
  412. **  : write midi_score to file
  413. **  : flush and close file
  414. **  : free midi_score
  415. **  : return TRUE
  416. **  ENDROUTINE
  417. **
  418. */
  419. {
  420.     auto unsigned char filemode[2] = "w",
  421.                        running_sts = 0,
  422.                        track_nam[32] = "AlgoRhythms by Tom Janzen";
  423.     auto unsigned int midi_index = 0,
  424.                       local_total_notes_qty = 0,
  425.                       intra_block_index = 0,
  426.                       time_int,
  427.                       time_len;
  428.     auto struct timeval tmp_time = {0, 0},
  429.                         last_time = {0, 0};
  430.     auto FILE *midi_file = NULL;
  431.     auto NOTE_RECORD_TYPE note_record;
  432.     auto MIDI_NOTE_TYPE note_event;
  433.     auto PAGE_LIST_TYPE *local_list_ptr = NULL;
  434.     auto MIDI_HEADER_TYPE midi_file_hdr = 
  435.                           {PACK_BYTES ('M','T','h','d'), 6, 
  436.                            MULTITRAK, 1, TICKS_PER_QUARTER};
  437.     auto MIDI_TRACK_TYPE track = {PACK_BYTES ('M', 'T', 'r', 'k'), 0};
  438.  
  439.     local_list_ptr = &page_list_head; /* initialize list pointer */
  440.     midi_index = 0;
  441.     /* 
  442.     ** can't be larger than * 8 + header; 
  443.     ** note = 4 bytes delay max, 3 for event max
  444.     */
  445.     midi_score = malloc((total_notes_qty * 10) + 18 + 4); 
  446.     if (NULL == midi_score)
  447.     {
  448.         return FALSE;
  449.     }
  450.     midi_score[midi_index] = 0;
  451.     midi_index++;
  452.     insert_meta_event(&midi_index, midi_score, TRACK_NAME, 
  453.                                 strlen(track_nam));
  454.     strcpy(&midi_score[midi_index], track_nam);
  455.     midi_index += strlen(track_nam);
  456.  
  457.     midi_score[midi_index] = 0;
  458.     midi_index++;
  459.     
  460.     insert_meta_event(&midi_index, midi_score, TEMPO_EVENT, 3);
  461.     midi_score[midi_index] = 0x0F;
  462.     midi_index++;
  463.     midi_score[midi_index] = 0x42;
  464.     midi_index++;
  465.     midi_score[midi_index] = 0x40; /* these 3 numbers give 
  466.                                    ** 1 million ticks/quarter note */
  467.     midi_index++;
  468.     midi_score[midi_index] = 0; /* required zero delay */
  469.  
  470.     midi_index++;
  471.     insert_meta_event(&midi_index, midi_score, TIME_SIG_EVENT, 4);
  472.     midi_score[midi_index] = 4;
  473.     midi_index++;
  474.     midi_score[midi_index] = 2;
  475.     midi_index++;
  476.     midi_score[midi_index] = 24;
  477.     midi_index++;
  478.     midi_score[midi_index] = 8;
  479.     midi_index++;
  480.     for (   local_total_notes_qty = 0; 
  481.             local_total_notes_qty < total_notes_qty;
  482.             local_total_notes_qty++)
  483.     {
  484.         if (intra_block_index >= NOTES_PER_BLOCK)
  485.         {
  486.             local_list_ptr = local_list_ptr->pl_r_succ;
  487.             if ((NULL == local_list_ptr)
  488.             ||  (NULL == local_list_ptr->pl_r_page))
  489.             {
  490.                 free(midi_score);
  491.                 midi_score = NULL;
  492.                 return FALSE; /* post error */
  493.             }
  494.             intra_block_index = 0;
  495.         }
  496.         memcpy(¬e_record, 
  497.                 &(local_list_ptr->pl_r_page[intra_block_index]),
  498.                 sizeof(NOTE_RECORD_TYPE));
  499.         intra_block_index++;
  500.         note_event.mn_uc_note_num = note_record.nr_uc_pitch;
  501.         note_event.mn_uc_sts_chanl = NOTEON | note_record.nr_uc_channel;
  502.         note_event.nm_velocity = note_record.nr_uc_velocity;
  503.         /*
  504.         ** convert absolute time struct to MIDI delay bytes
  505.         */
  506.         tmp_time = note_record.nr_r_event_time;
  507.         if (0 == local_total_notes_qty)
  508.         {
  509.             last_time = tmp_time;
  510.         }
  511.         if (1 == CmpTime(&tmp_time, &last_time)) /* -1, first > second */
  512.         /*
  513.         ** The new time can be earlier than the last time if the user
  514.         ** stopped the music and hit "Play" i.e., from the beginning
  515.         */
  516.         {
  517.             last_time = tmp_time;
  518.             time_int = 0;
  519.         }
  520.         else
  521.         {
  522.             SubTime(&tmp_time, &last_time); /* f(a, b), a = a - b */
  523.             last_time = note_record.nr_r_event_time;
  524.             time_int 
  525.                 = (tmp_time.tv_micro / 1000) + (tmp_time.tv_secs * 1000);
  526.             time_int = (time_int * 240) / 1000; /* give 240/second */
  527.         }
  528.         time_len 
  529.             = variable_len_values(time_int, &midi_score[midi_index]);
  530.         midi_index += time_len;
  531.         store_a_note(&midi_index, midi_score, note_event.mn_uc_sts_chanl, 
  532.             note_event.nm_velocity, note_event.mn_uc_note_num, 
  533.             &running_sts);
  534.     }
  535.     midi_score[midi_index] = 0;
  536.     midi_index++;
  537.     insert_meta_event(&midi_index, midi_score, EOT_EVENT, 0);
  538.     midi_file = fopen(midi_file_nam, filemode);
  539.     if (NULL == midi_file) 
  540.     {
  541.         free(midi_score);
  542.         midi_score = NULL;
  543.         /* report an error */
  544.         return FALSE;
  545.     }
  546.     fwrite((char *)&midi_file_hdr,sizeof(MIDI_HEADER_TYPE), 1, midi_file);
  547.     track.chunk_len = midi_index;
  548.     fwrite((char *)&track, sizeof(MIDI_TRACK_TYPE), 1, midi_file);
  549.     fwrite((char *)midi_score, midi_index, 1, midi_file);
  550.     fflush(midi_file);
  551.     fclose(midi_file);
  552.     free(midi_score);
  553.     midi_score = NULL;
  554.  
  555.     return TRUE;
  556. }
  557.  
  558. static int variable_len_values(int number, unsigned char *array)
  559. /*
  560. ** FUNCTIONAL DESCRIPTION:
  561. **  Converts an integer to a variable-length MIDI multi-byte value
  562. **
  563. ** RETURN VALUE:
  564. **      description: number of bytes in variable-length MIDI value
  565. **        data_type: int
  566. **
  567. ** ARGUMENTS:
  568. **
  569. **  number-
  570. **         description: number to convert to multi-byte MIDI file value
  571. **           data_type: int
  572. **              access: read only
  573. **
  574. **  array-
  575. **         description: The array into which to store the multi-byte value
  576. **           data_type: pointer to char
  577. **              access: write only
  578. **
  579. ** DESIGN:
  580. **
  581. **  ROUTINE variable_len_values
  582. **  : ls_byte = number & x7F
  583. **  : mid_byte = number & x3F80 >> 7 | x80
  584. **  : ms_byte = number & x1FC000 >> 14 | x80
  585. **  : byte_4 = number & xFE00000 >> 21 | x80
  586. **  : IF byte_4 had a number in it
  587. **  : : array[0] = byte_4
  588. **  : : array[1] = ms_byte
  589. **  : : array[2] = mid_byte
  590. **  : : array[3] = ls_byte
  591. **  : : return 4
  592. **  : ENDIF
  593. **  : IF ms_byte had a value in it
  594. **  : : array[0] = mid_byte
  595. **  : : array[1] = ms_byte
  596. **  : : array[2] = ls_byte
  597. **  : : return 3
  598. **  : ENDIF
  599. **  : IF mid_byte had a value in it
  600. **  : : array[0] = mid_byte
  601. **  : : array[1] = ls_byte
  602. **  : : return 2
  603. **  : ENDIF
  604. **  : array[0] = ls_byte
  605. **  : return 1
  606. **  ENDROUTINE
  607. */
  608. {
  609.     auto unsigned char  byte_4,
  610.                         ms_byte, 
  611.                         mid_byte, 
  612.                         ls_byte;
  613.                     
  614.     ls_byte = (number & 0x0000007F);
  615.     mid_byte = ((number & 0x00003F80) >> 7) | 0x80;
  616.     ms_byte = ((number & 0x001FC000) >> 14) | 0x80;
  617.     byte_4 = ((number &  0x0FE00000) >> 21) | 0x80;
  618.     if (byte_4 != 0x80)
  619.     {
  620.         array[0] = byte_4;
  621.         array[1] = ms_byte;
  622.         array[2] = mid_byte;
  623.         array[3] = ls_byte;
  624.         return 4;
  625.     }
  626.     if (ms_byte != 0x80)
  627.     {
  628.         array[0] = ms_byte;
  629.         array[1] = mid_byte;
  630.         array[2] = ls_byte;
  631.         return 3;
  632.     }
  633.  
  634.     if (mid_byte != 0x80)
  635.     {
  636.         array[0] = mid_byte;
  637.         array[1] = ls_byte;
  638.         return 2;
  639.     }
  640.     array[0] = ls_byte;
  641.     
  642.     return 1;
  643. }
  644.  
  645. static void insert_meta_event(unsigned int *midi_index,
  646.                                unsigned char *midi_score,
  647.                                const unsigned char event_type,
  648.                                const unsigned int event_len)
  649. /*
  650. ** FUNCTIONAL DESCRIPTION:
  651. **  Adds a meta event to a MIDI score
  652. **
  653. ** ARGUMENTS:
  654. **
  655. **  midi_index-
  656. **         description: position in midi_score area
  657. **           data_type: pointer to unsigned int
  658. **              access: read/write
  659. **
  660. **  midi_score-
  661. **         description: midi score area
  662. **           data_type: pointer to unsigned char
  663. **              access: write only
  664. **
  665. **  event_type-
  666. **         description: type of meta event being inserted
  667. **           data_type: char
  668. **              access: read only
  669. **
  670. **  event_len-
  671. **         description: length of meta event
  672. **           data_type: unsigned int
  673. **              access: read only
  674. **
  675. ** DESIGN:
  676. **
  677. **  ROUTINE insert_meta_event
  678. **  : midi_score[*midi_index] = META_EVENT marker
  679. **  : increment midi_index
  680. **  : midi_score[*midi_index] = event_type
  681. **  : increment midi_index
  682. **  : midi_score[*midi_index] = event_len
  683. **  : increment midi_index
  684. **  ENDROUTINE
  685. */
  686. {
  687.     midi_score[*midi_index] = META_EVENT;
  688.     (*midi_index)++;
  689.     midi_score[*midi_index] = event_type;
  690.     (*midi_index)++;
  691.     midi_score[*midi_index] = event_len;
  692.     (*midi_index)++;
  693.  
  694.     return;
  695. }
  696.  
  697. static void store_a_note(  unsigned int *midi_index,
  698.                             unsigned char *midi_score,
  699.                             unsigned char current_channel,
  700.                             unsigned char current_dynamic,
  701.                             unsigned char pitch,
  702.                             unsigned char *running_sts)
  703. /*
  704. ** FUNCTIONAL DESCRIPTION:
  705. **  Put a note event into midi_score
  706. **
  707. ** ARGUMENTS:
  708. **
  709. **  midi_index-
  710. **         description: position in internal MIDI score array
  711. **           data_type: pointer to int
  712. **              access: read/write
  713. **
  714. **  midi_score-
  715. **         description: internal MIDI score array for recording
  716. **           data_type: pointer to char
  717. **              access: write only
  718. **
  719. **  current_channel-
  720. **         description: MIDI channel
  721. **           data_type: unsigned char
  722. **              access: read only
  723. **
  724. **  current_dynamic-
  725. **         description: MIDI dynamic
  726. **           data_type: unsigned char
  727. **              access: read only
  728. **
  729. **  pitch-
  730. **         description: MIDI pitch
  731. **           data_type: unsigned char
  732. **              access: read only
  733. **
  734. **  running_sts-
  735. **         description: current running status
  736. **           data_type: pointer to unsigned char
  737. **              access: read/write
  738. **
  739. ** DESIGN:
  740. **
  741. ** ROUTINE store_a_note
  742. **  : copy channel, pitch, dynamic to midi_note
  743. **  : IF chanl == file running status
  744. **  : : copy only pitch and dynamic into midi_score
  745. **  : ELSE
  746. **  : : copy channel/noteon, pitch and dynamic into midi_score
  747. **  : : set new running status for file
  748. **  : ENDIF
  749. ** ENDROUTINE
  750. */
  751. {
  752.     static MIDI_NOTE_TYPE midi_note;
  753.  
  754.     midi_note.mn_uc_sts_chanl = current_channel;
  755.     midi_note.mn_uc_note_num = pitch;
  756.     midi_note.nm_velocity = current_dynamic;
  757.     if (midi_note.mn_uc_sts_chanl == *running_sts)
  758.     {
  759.         memcpy( &midi_score[*midi_index], &(midi_note.mn_uc_note_num), 2);
  760.         (*midi_index) += 2;
  761.     }
  762.     else  /* running status changed */
  763.     {
  764.         memcpy(&midi_score[*midi_index], &midi_note, sizeof midi_note);
  765.         (*midi_index) += sizeof(MIDI_NOTE_TYPE);
  766.         *running_sts = midi_note.mn_uc_sts_chanl;
  767.     }
  768.     return;
  769. }
  770.